libobs_simple\sources\linux\sources/linux_general_screen_capture.rs
1use libobs_wrapper::{
2 data::ObsObjectBuilder,
3 runtime::ObsRuntime,
4 sources::ObsSourceRef,
5 utils::{ObsError, SourceInfo},
6};
7use std::env;
8
9use crate::sources::linux::{
10 sources::x11_capture::X11CaptureSourceBuilder, PipeWireDesktopCaptureSourceBuilder,
11};
12
13/// Display server type detection
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum DisplayServerType {
16 /// Wayland display server
17 Wayland,
18 /// X11/Xorg display server
19 X11,
20 /// Unknown or undetected display server
21 Unknown,
22}
23
24impl DisplayServerType {
25 /// Detect the current display server type using environment variables.
26 ///
27 /// Checks in order:
28 /// 1. `XDG_SESSION_TYPE` (most reliable)
29 /// 2. `WAYLAND_DISPLAY` (indicates Wayland)
30 /// 3. `DISPLAY` (indicates X11)
31 pub fn detect() -> Self {
32 // First, check XDG_SESSION_TYPE (most reliable)
33 if let Ok(session_type) = env::var("XDG_SESSION_TYPE") {
34 let session_type = session_type.to_lowercase();
35 if session_type.contains("wayland") {
36 return DisplayServerType::Wayland;
37 } else if session_type.contains("x11") {
38 return DisplayServerType::X11;
39 }
40 }
41
42 // Check WAYLAND_DISPLAY (if set, we're on Wayland)
43 if env::var("WAYLAND_DISPLAY").is_ok() {
44 return DisplayServerType::Wayland;
45 }
46
47 // Check DISPLAY (if set and no Wayland indicators, we're on X11)
48 if env::var("DISPLAY").is_ok() {
49 return DisplayServerType::X11;
50 }
51
52 DisplayServerType::Unknown
53 }
54
55 /// Returns whether PipeWire should be preferred for this display server.
56 ///
57 /// PipeWire is the modern capture API and works on both X11 and Wayland,
58 /// but is essential for Wayland and optional for X11.
59 pub fn prefer_pipewire(&self) -> bool {
60 match self {
61 DisplayServerType::Wayland => true, // PipeWire is required for Wayland
62 DisplayServerType::X11 => false, // X11 has native capture
63 DisplayServerType::Unknown => true, // Default to PipeWire for safety
64 }
65 }
66}
67
68/// General Linux screen capture source that automatically selects the best capture method.
69///
70/// This wrapper automatically chooses between:
71/// - **PipeWire capture** (for Wayland or modern Linux setups)
72/// - **X11 screen capture** (for traditional X11 setups)
73///
74/// The selection is based on the detected display server type.
75///
76/// # Example
77///
78/// ```no_run
79/// use libobs_simple::sources::linux::LinuxGeneralScreenCapture;
80/// use libobs_wrapper::{context::ObsContext, sources::ObsSourceBuilder, utils::StartupInfo};
81///
82/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
83/// # let startup_info = StartupInfo::default();
84/// # let mut context = ObsContext::new(startup_info)?;
85/// # let mut scene = context.scene("Main Scene")?;
86///
87/// // Automatically selects PipeWire or X11 based on display server
88/// let capture = LinuxGeneralScreenCapture::auto_detect(
89/// &mut context,
90/// "Screen Capture"
91/// )?;
92///
93/// // Add to scene
94/// scene.add(&capture)?;
95/// # Ok(())
96/// # }
97/// ```
98#[derive(Debug)]
99pub struct LinuxGeneralScreenCapture {
100 info: SourceInfo,
101 capture_type: CaptureType,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105enum CaptureType {
106 PipeWire,
107 X11,
108}
109
110impl LinuxGeneralScreenCapture {
111 /// Create a screen capture source by auto-detecting the display server type.
112 ///
113 /// This is the recommended way to create a screen capture on Linux.
114 //TODO rework this API, the best way to use this is to create a SourceBuilder that does the detection internally
115 pub fn auto_detect(
116 runtime: ObsRuntime,
117 name: &str,
118 restore_token: Option<String>,
119 ) -> Result<Self, Box<dyn std::error::Error>> {
120 let display_type = DisplayServerType::detect();
121 Self::new(runtime, name, display_type, restore_token)
122 }
123
124 /// Create a screen capture source for a specific display server type.
125 ///
126 /// # Arguments
127 ///
128 /// * `runtime` - The OBS runtime
129 /// * `name` - Name for the source
130 /// * `display_type` - The display server type to create a source for
131 /// * `restore_token` - Optional restore token for restoring source settings (this is only for pipewire)
132 pub fn new(
133 runtime: ObsRuntime,
134 name: &str,
135 display_type: DisplayServerType,
136 restore_token: Option<String>,
137 ) -> Result<Self, Box<dyn std::error::Error>> {
138 if display_type.prefer_pipewire() {
139 Self::new_pipewire(runtime, name, restore_token)
140 } else {
141 Self::new_x11(runtime, name)
142 }
143 }
144
145 /// Create a PipeWire-based screen capture source.
146 pub fn new_pipewire(
147 runtime: ObsRuntime,
148 name: &str,
149 restore_token: Option<String>,
150 ) -> Result<Self, Box<dyn std::error::Error>> {
151 let builder =
152 PipeWireDesktopCaptureSourceBuilder::new(name, runtime.clone())?.set_show_cursor(true);
153 let builder = if let Some(token) = restore_token {
154 builder.set_restore_token(token)
155 } else {
156 builder
157 };
158
159 let info = builder.build()?;
160 Ok(LinuxGeneralScreenCapture {
161 info,
162 capture_type: CaptureType::PipeWire,
163 })
164 }
165
166 /// Create an X11-based screen capture source.
167 pub fn new_x11(runtime: ObsRuntime, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
168 let builder = X11CaptureSourceBuilder::new(name, runtime.clone())?;
169 let info = builder.set_show_cursor(true).set_screen(0).build()?;
170 Ok(LinuxGeneralScreenCapture {
171 info,
172 capture_type: CaptureType::X11,
173 })
174 }
175
176 pub fn add_to_scene(
177 self,
178 scene: &mut libobs_wrapper::scenes::ObsSceneRef,
179 ) -> Result<ObsSourceRef, ObsError> {
180 scene.add_source(self.info)
181 }
182
183 /// Get the type of capture being used.
184 pub fn capture_type_name(&self) -> &str {
185 match self.capture_type {
186 CaptureType::PipeWire => "PipeWire",
187 CaptureType::X11 => "X11",
188 }
189 }
190}
191
192impl AsRef<SourceInfo> for LinuxGeneralScreenCapture {
193 fn as_ref(&self) -> &SourceInfo {
194 &self.info
195 }
196}